package gobelinmaker.server;

import gobelinmaker.data.BasicHoleData;
import gobelinmaker.data.BasicWoolData;
import gobelinmaker.data.JSONUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.json.simple.JSONObject;

/**
 * Munkafolyamat osztálya.
 *
 * @author imruf84
 */
public class ServerJob {

    /**
     * Munkakönyvtár.
     */
    private final String workingDir;
    /**
     * Munka neve.
     */
    private String name;

    /**
     * Konstruktor.
     *
     * @param workingDir munkakönyvtár neve
     * @param name munka neve (ha üresen marad, akkor az aktív munkafolyamat
     * lesz az ha létezik)
     * @throws java.io.IOException kivétel
     */
    public ServerJob(String workingDir, String name) throws IOException {
        this.workingDir = workingDir;
        this.name = name;

        // Ha nem létezik a munkakönyvtár akkor létrehozzuk.
        Path wp = Paths.get(this.workingDir);
        if (!Files.exists(wp)) {
            Files.createDirectory(wp);
        }

        // Ha van már tárolt adat és nincs munkanév megadva...
        if (Files.exists(Paths.get(this.workingDir + "/_")) && name.isEmpty()) {
            // ...akkor a meglévőt fogjuk használni.
            JSONObject json = JSONUtils.JSONFromFile(this.workingDir + "/_");
            this.setJobName((String) json.get("name"));
            return;
        } else {
            // Egyébként töröljük az előző munkamenetet.
            FileUtils.cleanDirectory(new File(this.workingDir));
        }

        // Adatok tárolása.
        if (!name.isEmpty()) {
            JSONObject json = new JSONObject();
            json.put("name", Paths.get(name).getFileName().toString());
            JSONUtils.JSONToFile(json, this.workingDir + "/_");
        }
    }

    /**
     * Munkafolyamat nevének a lekérdezése.
     *
     * @return név
     */
    public String getJobName() {
        return this.name;
    }

    /**
     * Munkamenet nevének a megadása.
     *
     * @param name munkamenet neve
     */
    private void setJobName(String name) {
        this.name = name;
    }

    /**
     * Fonal tárolása.
     *
     * @param bwd fonal
     * @throws java.io.IOException kivétel
     */
    public void storeWool(BasicWoolData bwd) throws IOException {

        if (null == bwd) {
            return;
        }

        Path wp = Paths.get(this.workingDir + "/" + bwd.woolIndex);
        // Ha még nincs ilyen könyvtár akkor létrehozzuk.
        if (!Files.exists(wp)) {
            Files.createDirectory(wp);
        }

        // Majd tároljuk a fonal adatait.
        wp = Paths.get(this.workingDir + "/" + bwd.woolIndex + "/_");
        JSONUtils.basicWoolDataToFile(wp, bwd);
    }

    /**
     * Lyuk tárolása.
     *
     * @param bhd lyuk
     * @throws java.io.IOException kivétel
     */
    public void storeHole(BasicHoleData bhd) throws IOException {
        if (null == bhd) {
            return;
        }

        Path wp = Paths.get(this.workingDir + "/" + bhd.woolIndex + "/" + bhd.holeIndex);
        JSONUtils.basicHoleDataToFile(wp, bhd);
    }

    /**
     * Munkafolyamat futásának az ellenőrzése.
     *
     * @return igaz ha végzett egyébként hamis
     */
    public boolean isFinished() {

        // Akkor nincs munka, ha üres a munkafolyamat könyvtára, vagy nem is létezik.
        Path wp = Paths.get(this.workingDir);

        if (!Files.exists(wp)) {
            return true;
        }

        return ServerJob.isDirEmpty(wp);
    }

    /**
     * Könyvtár ürességének az ellenörzése.
     *
     * @param directory könyvtár
     * @return igaz ha a könyvtár üres egyébként hamis
     */
    private static boolean isDirEmpty(final Path directory) {
        
        if (!Files.exists(directory)) return true;
        
        return !(0 < directory.toFile().list().length);
    }

    /**
     * Könyvtár tartalmának rekurzív kilistázása.
     *
     * @param path elérési út
     * @param files fájlok tárolója
     */
    private static LinkedList<File> listFiles(Path path)  {

        final LinkedList<File> files = new LinkedList<>();
        Iterator<File> it = FileUtils.iterateFilesAndDirs(path.toFile(), TrueFileFilter.INSTANCE, DirectoryFileFilter.DIRECTORY);
        while (it.hasNext()) {
            File f = it.next();

                files.add(f);
            
        }
        
        // A vizsgált könyvtár nem számít bele.
        files.pop();
        
        return files;
        
    }

    /**
     * Munkaterület állományainak törlése.
     *
     * @throws java.io.IOException kivétel
     */
    public void clear() throws IOException {

        FileUtils.cleanDirectory(new File(this.workingDir));
        this.setJobName("");
    }

    /**
     * Munkaterület adott számú állományának a törlése.
     *
     * @param count maximálisan törlendő állományok száma
     * @return törölt elemek száma
     */
    public int removeNext(int count) {
        
        // Ha üres a könyvtár akkor végeztünk.
        if (ServerJob.isDirEmpty(Paths.get(this.workingDir))) {
            this.setJobName("");
            return 0;
        }
        
        // Fájlok lekérdezése.
        final LinkedList<File> files = new LinkedList<>();
        Iterator<File> it = FileUtils.iterateFilesAndDirs(new File(this.workingDir), TrueFileFilter.INSTANCE, DirectoryFileFilter.DIRECTORY);
        while (it.hasNext()) {
            File f = it.next();

            // Csak fájlokat és üres könyvtárakat törlünk.
            if (f.isFile() || (f.isDirectory() && ServerJob.isDirEmpty(f.toPath()))) {

                files.add(f);

                // Egyszerre csak addott számút törlünk.
                if (files.size() > count - 1) {
                    break;
                }
            }
        }
        
        // Törlendő állományok száma.
        int size = files.size();
        
        // Állományok törlése.
        while (true) {
            files.pop().delete();
            if (files.isEmpty()) {
                break;
            }
        }
        
        return size;
    }

    /**
     * Munkaterület állományainak a számának a lekérdezése.
     *
     * @return állományok száma
     */
    public int getFilesCount() {
        final LinkedList<File> files = ServerJob.listFiles(Paths.get(this.workingDir));

        return files.size();
    }

}
